from django.shortcuts import render
from django.views import View
from meiduo_mall.utils.views import LoginRequiredJsonMixin
from users.models import Address
from django_redis import get_redis_connection
from goods.models import SKU
from django.http import JsonResponse
from decimal import Decimal
from orders.models import OrderInfo, OrderGoods
from django.utils import timezone
from django.db import transaction
import json, logging

logger = logging.getLogger('django')


class OrderSettlementView(LoginRequiredJsonMixin, View):
    """获取结算数据"""

    def get(self, request):
        """提供订单结算页面"""
        # 获取登录用户，必须登录
        user = request.user

        # 1、购物车数据，从Redis购物车中查询出被勾选的商品信息
        skus = []
        redis_conn = get_redis_connection("carts")
        redis_carts = redis_conn.hgetall("carts_%s" % user.id)
        redis_selected = redis_conn.smembers("selected_%s" % user.id)
        sku_ids = redis_carts.keys()
        for sku_id in sku_ids:
            if sku_id in redis_selected:
                sku = SKU.objects.get(id=sku_id)
                skus.append({
                    'id': sku.id,
                    'name': sku.name,
                    'default_image_url': sku.default_image.url,
                    'count': int(redis_carts[sku_id]),  # int(b'11') --> 11
                    'price': sku.price
                })
        # 2、收货地址数据，查询当前用户的所有地址信息
        addresses = []
        user_addresses = Address.objects.filter(user=user, is_deleted=False)
        for user_addr in user_addresses:
            addresses.append({
                'id': user_addr.id,
                'province': user_addr.province.name,
                'city': user_addr.city.name,
                'district': user_addr.district.name,
                'place': user_addr.place,
                'receiver': user_addr.receiver,
                'mobile': user_addr.mobile
            })
        # 3、运费
        freight = Decimal("10.00")  # 表示十进制浮点数10.0，预防计算时精度丢失

        # 4、响应返回
        context = {'addresses': addresses, 'skus': skus, 'freight': freight}
        return JsonResponse({"code": 0, "errmsg": "提供订单结算页面成功", "context": context})


class OrderCommitView(LoginRequiredJsonMixin, View):
    """订单提交"""

    def post(self, request):
        """保存订单信息和订单商品信息"""
        # 1、提取参数
        json_dict = json.loads(request.body.decode("utf-8"))
        address_id = json_dict.get("address_id")
        pay_method = json_dict.get("pay_method")

        # 2、校验参数
        if not all([address_id, pay_method]):
            return JsonResponse({"code": 400, "errmsg": "缺少必传参数"})
        # 判断address_id是否合法
        try:
            address = Address.objects.get(id=address_id)
        except Exception as e:
            logging.error(e)
            return JsonResponse({'code': 400, 'errmsg': '参数address_id错误'})
        # 判断pay_method是否合法
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM["CASH"], OrderInfo.PAY_METHODS_ENUM["ALIPAY"]]:
            return JsonResponse({'code': 400, 'errmsg': '参数pay_method错误'})

        # 3、业务数据处理
        # 获取登录用户
        user = request.user
        # 生成订单编号：年月日时分秒+用户编号
        order_id = timezone.localtime().strftime("%Y%m%d%H%M%S") + ("%09d" % user.id)

        # TODO ①：--------设置事物起始--------
        with transaction.atomic():
            # TODO ①：--------事物保存点--------
            save_id = transaction.savepoint()

            try:
                # 3.1、保存订单基本信息 OrderInfo
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal("0"),
                    freight=Decimal("10.00"),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"] if pay_method == OrderInfo.PAY_METHODS_ENUM[
                        "ALIPAY"] else
                    OrderInfo.ORDER_STATUS_ENUM["UNSEND"]
                )

                # 3.2、保存订单商品信息 OrderGoods
                # 从 redis 读取购物车中被勾选的商品信息
                redis_conn = get_redis_connection("carts")
                redis_carts = redis_conn.hgetall("carts_%s" % user.id)
                redis_selected = redis_conn.smembers("selected_%s" % user.id)
                # 获取购物车中选中的商品信息
                carts_dict = {}
                sku_ids = redis_carts.keys()
                for sku_id in sku_ids:
                    if sku_id in redis_selected:
                        carts_dict[int(sku_id)] = {
                            "count": int(redis_carts[sku_id])
                        }
                # 遍历购物车中被勾选的商品，判断购买数量是否大于商品库存
                sku_ids = carts_dict.keys()
                for sku_id in sku_ids:

                    # TODO ②：使用乐观锁，防止多用户并发下单造成库存数据资源抢占
                    while True:
                        sku = SKU.objects.get(id=sku_id)
                        # 获取商品sku的 原始库存量 和  原始销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales

                        sku_count = carts_dict[sku_id]["count"]
                        # 如果库存不足
                        if sku_count > sku.stock:
                            return JsonResponse({'code': 400, 'errmsg': '库存不足'})

                        # TODO ②：使用乐观锁，更新商品SKU 减少库存，增加销量
                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock, sales=origin_sales).update(
                            stock=new_stock, sales=new_sales)
                        # 如果下单下单失败,库存足够则继续下单,直到下单成功或者库存不足
                        if result == 0:
                            continue

                        # 增加商品SPU销量
                        sku.spu.sales += sku_count
                        sku.spu.save()
                        # 保存订单商品信息 OrderGoods（多）
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price
                        )
                        # 在订单基本信息中累加该sku商品的总数和总价
                        order.total_count += sku_count
                        order.total_amount += Decimal(sku_count) * Decimal(sku.price)

                        # 下单 成功或失败 跳出
                        break

                # 累加邮费，保存订单信息
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                logging.error(e)
                # TODO ①：--------事物回滚--------
                transaction.savepoint_rollback(save_id)
                return JsonResponse({'code': 0, 'errmsg': '下单失败'})

            # TODO ①：--------提交事物--------
            transaction.savepoint_commit(save_id)

        # 清除购物车中已结算的商品
        redis_conn.hdel("carts_%s" % user.id, *sku_ids)

        # 4、响应返回
        return JsonResponse({'code': 0, 'errmsg': '下单成功', 'order_id': order.order_id})
